#include "StdAfx.h"
#include "Serialization.h"
#include "EditorPreferences.h"
#include "yasli/FileSelector.h"
#include "utils/Macros.h"
#include "TagStorage.h"
#ifdef WIN32
#include <windows.h>
#endif
#include <algorithm>


void EditorProfile::serialize(Archive& ar)
{
#ifdef WIN32
	static const FileSelector::Options options("Executables|*.exe");
#else
	static const FileSelector::Options options("Executables|*");
#endif
	ar(FileSelector(executable, options), "executable");
	ar(arguments, "arguments");
}

// ---------------------------------------------------------------------------
EditorPreferences editorPreferences;

EditorPreferences::EditorPreferences()
: PreferencesBlock("editor", true)
{
	findAvailableEditors();

}

StringList EditorPreferences::availableEditorsStringList(bool withCustom)
{
	StringList result;
	AvailableEditors::iterator it;
	FOR_EACH(availableEditors_, it){
		result.push_back(it->name);
	}
	if(withCustom)
		result.push_back("Custom");
	return result;
}

int EditorPreferences::editorIndex(const EditorProfile& profile)
{
	AvailableEditors::iterator it = std::find(availableEditors_.begin(), availableEditors_.end(), profile);
	return std::distance(availableEditors_.begin(), it);
}

void EditorPreferences::serialize(Archive& ar)
{
	int index = -1;
	if(ar.isEdit()){
		index = editorIndex(editorProfile_);
		StringListValue selectedEditor(availableEditorsStringList(index >= int(availableEditors_.size())), index);
		ar(selectedEditor, "editor");
		if(ar.isInput() && index != selectedEditor.index())
			index = selectedEditor.index();
		else
			index = -1;
	}
	editorProfile_.serialize(ar);
	if(ar.isInput() && ar.isEdit() && index >= 0 && index < int(availableEditors_.size()))
		editorProfile_ = availableEditors_[index];
}

void strReplace(std::string& str, const char* what, const char* forWhat)
{
	std::string::size_type pos;
	size_t whatLen = strlen(what);
	while((pos = str.find(what)) != std::string::npos){
		str.replace(str.begin() + pos, str.begin() + pos + whatLen, forWhat, forWhat + strlen(forWhat));
	}

}

void EditorPreferences::executeEditor(const Tag& tag)
{
	//std::string tagDirectory = Files::extractFilePath(tagsFileName);
	std::string commandLine = editorProfile_.executable;
	std::string arguments = editorProfile_.arguments;

	strReplace(arguments, "$(File)", tag.location.fileName);
	MemoryWriter buf;
	buf << tag.location.line;
	strReplace(arguments, "$(Line)", buf.c_str());

	commandLine += " ";
	commandLine += arguments;


#ifdef WIN32
	
	PROCESS_INFORMATION processInfo;
	ZeroMemory(&processInfo, sizeof(processInfo));
	
	STARTUPINFO startupInfo;
	ZeroMemory(&startupInfo, sizeof(startupInfo));

	CreateProcess(0, (char*)commandLine.c_str(), 0, 0, FALSE, 0, 0, 0, &startupInfo, &processInfo);


	//WaitForSingleObject(processInfo.hProcess, INFINITE);
	CloseHandle(processInfo.hProcess);
	CloseHandle(processInfo.hThread);
#endif
}

// ---------------------------------------------------------------------------
// Editor-specific stuff

static bool findEditorVim(EditorProfile& profile)
{
	profile.name = "Vim";
	profile.arguments = "--servername gVimKick --remote-silent +:$(Line) $(File)";
#ifdef WIN32
	HKEY key;

	 if(RegOpenKeyEx(HKEY_CLASSES_ROOT, "CLSID\\{0F0BFAE1-4C90-11D1-82D7-0004AC368519}\\LocalServer32", 0, KEY_READ, &key) == ERROR_SUCCESS){
		char path[MAX_PATH];
		unsigned long size = sizeof(path) - 1;
		unsigned long type = REG_SZ;
		if(RegQueryValueEx(key, "", 0, &type, (BYTE*)path, &size) == ERROR_SUCCESS){
			profile.executable = path;
			return true;
		}
	}

	if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\GVim", 0, KEY_READ, &key) == ERROR_SUCCESS){
		char path[MAX_PATH];
		unsigned long size = sizeof(path) - 1;
		unsigned long type = REG_SZ;
		if(RegQueryValueEx(key, "path", 0, &type, (BYTE*)path, &size) == ERROR_SUCCESS){
			profile.executable = path;
			return true;
		}
	}
	return false;
#else
	profile.executable = "gvim"
#endif
}

static bool findEditorNotepad(EditorProfile& profile)
{
#ifdef WIN32
	profile.name = "Notepad";
	profile.executable = "notepad.exe";
	profile.arguments = "$(File)";
	return true;
#else
	return false;
#endif
}

typedef bool(*FindEditorFunction)(EditorProfile& profile);

static FindEditorFunction findEditorFunctions[] = {
	&findEditorVim,
	&findEditorNotepad,
};

void EditorPreferences::findAvailableEditors()
{
	FindEditorFunction* func = findEditorFunctions;
	FindEditorFunction* end = findEditorFunctions + sizeof(findEditorFunctions) / sizeof(findEditorFunctions[0]);

	while(func != end){
		EditorProfile profile;
		if((*func)(profile)){
			availableEditors_.push_back(profile);
		}
		++func;
	}
}
